home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-04
/
ms_sh21s.zip
/
SH210
/
SRC
/
SH7.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-14
|
85KB
|
4,098 lines
/* MS-DOS SHELL - Internal Command Processing
*
* MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
*
* This code is based on (in part) the shell program written by Charles
* Forsyth and is subject to the following copyright restrictions. The
* code for the test (dotest) command was based on code written by
* Erik Baalbergen. The following copyright conditions apply:
*
* 1. Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice is duplicated in the
* source form and the copyright notice in file sh6.c is displayed
* on entry to the program.
*
* 2. The sources (or parts thereof) or objects generated from the sources
* (or parts of sources) cannot be sold under any circumstances.
*
* $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh7.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
*
* $Log: sh7.c,v $
* Revision 2.5 1992/12/14 10:54:56 istewart
* BETA 215 Fixes and 2.1 Release
*
* Revision 2.4 1992/11/06 10:03:44 istewart
* 214 Beta test updates
*
* Revision 2.3 1992/09/03 18:54:45 istewart
* Beta 213 Updates
*
* Revision 2.2 1992/07/16 14:33:34 istewart
* Beta 212 Baseline
*
* Revision 2.1 1992/07/10 10:52:48 istewart
* 211 Beta updates
*
* Revision 2.0 1992/05/07 21:33:35 Ian_Stewartson
* MS-Shell 2.0 Baseline release
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <dirent.h>
#include <stdarg.h>
#include <time.h>
#ifdef OS2
#define INCL_DOSSESMGR
#define INCL_DOSPROCESS
#define INCL_DOSSIGNALS
#include <os2.h>
#else
#include <dos.h>
#endif
#include "sh.h"
#define SECS 60L
#define MINS 3600L
#define IS_OCTAL(a) (((a) >= '0') && ((a) <= '7'))
/* Definitions for echo and print */
#define ECHO_ESCAPE 0x01
#define ECHO_NO_EOL 0x02
#define ECHO_HISTORY 0x04
/* Definitions for test */
#define END_OF_INPUT 0
#define FILE_READABLE 1
#define FILE_WRITABLE 2
#define FILE_REGULAR 3
#define FILE_DIRECTRY 4
#define FILE_NONZERO 5
#define FILE_TERMINAL 6
#define STRING_ZERO 7
#define STRING_NONZERO 8
#define STRING_EQUAL 9
#define STRING_NOTEQUAL 10
#define NUMBER_EQUAL 11
#define NUMBER_NOTEQUAL 12
#define NUMBER_EQ_GREAT 13
#define NUMBER_GREATER 14
#define NUMBER_EQ_LESS 15
#define NUMBER_LESS 16
#define UNARY_NOT 17
#define BINARY_AND 18
#define BINARY_OR 19
#define LPAREN 20
#define RPAREN 21
#define OPERAND 22
#define FILE_EXECUTABLE 23
#define FILE_USER 24
#define FILE_GROUP 25
#define FILE_TEXT 26
#define FILE_BLOCK 27
#define FILE_CHARACTER 28
#define FILE_FIFO 29
#define FILE_NEWER 30
#define FILE_OLDER 31
#define STRING_LESS 32
#define STRING_GREATER 33
#define FILE_EXISTS 34
#define TEST_OPTION 35
#define FILE_SYMBOLIC 36
#define FILE_OWNER 37
#define FILE_GROUPER 38
#define FILE_SOCKET 39
#define FILE_EQUAL 40
#define UNARY_OP 1
#define BINARY_OP 2
#define B_UNARY_OP 3
#define B_BINARY_OP 4
#define PAREN 5
/* This is the list of operators and the conversion values */
static struct TestOperator {
char *OperatorName;
short OperatorID;
short OperatorType;
} ListOfTestOperators[] = {
/* These two entries are modified depending on the test program. The
* alternative values are shown in the following comment.
*/
{"-a", FILE_EXISTS, UNARY_OP},
/* {"-a", BINARY_AND, B_BINARY_OP}, */
{"-o", TEST_OPTION, UNARY_OP},
/* {"-o", BINARY_OR, B_BINARY_OP}, */
/* Add new entries after here */
{"-r", FILE_READABLE, UNARY_OP},
{"-w", FILE_WRITABLE, UNARY_OP},
{"-x", FILE_EXECUTABLE, UNARY_OP},
{"-f", FILE_REGULAR, UNARY_OP},
{"-d", FILE_DIRECTRY, UNARY_OP},
{"-s", FILE_NONZERO, UNARY_OP},
{"-t", FILE_TERMINAL, UNARY_OP},
{"-z", STRING_ZERO, UNARY_OP},
{"-n", STRING_NONZERO, UNARY_OP},
{"=", STRING_EQUAL, BINARY_OP},
{"!=", STRING_NOTEQUAL, BINARY_OP},
{"<", STRING_LESS, BINARY_OP},
{">", STRING_GREATER, BINARY_OP},
{"-eq", NUMBER_EQUAL, BINARY_OP},
{"-ne", NUMBER_NOTEQUAL, BINARY_OP},
{"-ge", NUMBER_EQ_GREAT, BINARY_OP},
{"-gt", NUMBER_GREATER, BINARY_OP},
{"-le", NUMBER_EQ_LESS, BINARY_OP},
{"-lt", NUMBER_LESS, BINARY_OP},
{"!", UNARY_NOT, B_UNARY_OP},
{"(", LPAREN, PAREN},
{")", RPAREN, PAREN},
{"&&", BINARY_AND, B_BINARY_OP},
{"||", BINARY_OR, B_BINARY_OP},
{"-c", FILE_CHARACTER, UNARY_OP},
{"-b", FILE_BLOCK, UNARY_OP},
{"-u", FILE_USER, UNARY_OP},
{"-g", FILE_GROUP, UNARY_OP},
{"-k", FILE_TEXT, UNARY_OP},
{"-p", FILE_FIFO, UNARY_OP},
{"-h", FILE_SYMBOLIC, UNARY_OP},
{"-L", FILE_SYMBOLIC, UNARY_OP},
{"-O", FILE_OWNER, UNARY_OP},
{"-G", FILE_GROUPER, UNARY_OP},
{"-S", FILE_SOCKET, UNARY_OP},
{"-nt", FILE_NEWER, BINARY_OP},
{"-ot", FILE_OLDER, BINARY_OP},
{"-ef", FILE_EQUAL, BINARY_OP},
{(char *)NULL, 0, 0}
};
/*
* -o values for set
*/
static struct SetOptions {
char *OptionName; /* Option name */
unsigned char FlagValue; /* Option flag */
bool HasOptionValue; /* Has -x value */
} SetOptions[] = {
{ "allexport", 'a', TRUE },
{ "errexit", 'e', TRUE },
{ "keyword", 'k', TRUE },
{ "noexec", 'n', TRUE },
{ "noglob", 'f', TRUE },
{ "nounset", 'u', TRUE },
{ "privileged", 'p', TRUE },
{ "verbose", 'v', TRUE },
{ "trackall", 'h', TRUE },
{ "xtrace", 'x', TRUE },
{ "ignoreeof", FLAGS_IGNOREEOF, FALSE },
{ "markdirs", FLAGS_MARKDIRECTORY, FALSE },
{ "noclobber", FLAGS_NOCLOBER, FALSE },
#ifdef OS2
{ "realpipes", FLAGS_REALPIPES, FALSE },
#endif
{ (char *)NULL, 0, FALSE }
};
/*
* Getopts values
*/
static GetoptsIndex GetOptsIndex = { 1, 1 };
/*
* General Functions
*/
static int near doOutofMemory (char *);
static int near TestProcessNextExpression (int);
static int near TestANDExpression (int);
static int near TestPrimaryExpression (int);
static int near TestLexicalAnalysis (char *);
static long near GetNumberForTest (char *);
static void near TestSyntaxError (void);
static bool near ReadALine (int, char *, bool, bool, int *);
static bool near ChangeOptionValue (char *, bool);
static void near SetClearFlag (int, bool);
static int near GetSignalNumber (char *);
static int near BreakContinueProcessing (char *, int);
static int near SetUpNewParameterVariables (char **, int, int, char *);
static int near UsageError (char *);
static void near PrintEntry (VariableList *, bool, unsigned int);
static int ComparisonForEnvironmentSort (VariableList **,
VariableList **);
static int near UpdateVariableStatus (char **, unsigned int);
static int near TypesetVariables (char **);
static int near ListAllVariables (unsigned int, bool);
static int near HandleFunction (char *);
static int near TestOptionValue (char *);
static void near ResetGetOptions (void);
static int near GetUnitNumber (char *);
static char near CleanUpBuffer (int, char *, int);
static struct SetOptions * near LookUpOptionValue (char *);
static void DisplayVariableEntry (void *, VISIT, int);
#ifdef OS2
static bool near ConvertJobToPid (char *, PID *);
#endif
/*
* Builtin Commands
*/
static int doexport (int, char **);
static int doreadonly (int, char **);
static int domsdos (int, char **);
static int dotypeset (int, char **);
static int dounalias (int, char **);
static int doalias (int, char **);
static int dolabel (int, char **);
static int dofalse (int, char **);
static int dochdir (int, char **);
static int dodrive (int, char **);
static int doshift (int, char **);
static int doumask (int, char **);
static int dodot (int, char **);
static int doecho (int, char **);
static int dolet (int, char **);
#ifdef OS2
static int dodetach (int, char **);
static int dostart (int, char **);
static int dokill (int, char **);
static int dojobs (int, char **);
static int dowait (int, char **);
#endif
static int dogetopts (int, char **);
static int dopwd (int, char **);
static int doswap (int, char **);
static int dounset (int, char **);
static int dowhence (int, char **);
static int dofc (int, char **);
static int dotest (int, char **);
static int dover (int, char **);
static int doread (int, char **);
static int doeval (int, char **);
static int dotrap (int, char **);
static int dobuiltin (int, char **);
static int dobreak (int, char **);
static int docontinue (int, char **);
static int doexit (int, char **);
static int doreturn (int, char **);
static int doset (int, char **);
static int dofunctions (int, char **);
static int dohistory (int, char **);
/*
* TWALK global values for DisplayVariable
*/
static unsigned int DVE_Mask;
static bool DVE_PrintValue;
/*
* Local data structure for test command
*/
static char **TestArgumentList;
static struct TestOperator *CurrentTestOperator;
static jmp_buf TestErrorReturn;
static char *TestProgram;
static bool NewTestProgram;
/*
* Static structure for typeset
*/
static struct TypesetValues {
unsigned int Flags_On;
unsigned int Flags_Off;
int Base;
int Width;
} TypesetValues;
/*
* Current position in Getoption string
*/
static int GetOptionPosition = 1; /* Current position */
static int BadOptionValue = 0; /* The bad option value */
/*
* Common strings
*/
static char *DoubleHypen = "--";
static char *DoublePlus = "++";
static char *TypeSetUsage = "typeset [ [ [+|-][Hflprtux] ] [+|-][LRZi[n]] [ name [=value] ...]";
static char *NotBuiltinCommand = "not a builtin\n";
static char *NotAnAlias = "%s: %s is not an alias\n";
static char *NotValidAlias = "Invalid alias name";
static char *Reply_Array[] = {LIT_REPLY, (char *)NULL};
static char *BadDrive = "%c: bad drive\n";
static char *ShellInternalCommand = "is a shell internal command";
static char *FCTooLong = "fc: command line too long\n";
static char LIT_alias[] = "alias";
static char LIT_print[] = "print";
static char LIT_read[] = "read";
static char LIT_shift[] = "shift";
static char LIT_break[] = "break";
static char LIT_builtin[] = "builtin";
static char *BuiltinUsage = "builtin [ -ads ] [ commands ]";
static char LIT_continue[] = "continue";
static char LIT_type[] = "type";
static char LIT_unalias[] = "unalias";
static char LIT_unfunction[] = "unfunction";
static char *LIT_bun = "bad unit number";
#ifdef OS2
static char *JobUsage = "jobs [-lp] [ -P pid]";
static char *KillUsage = "kill [ [-l] | [ [-sig] [ process id | %job number ] ... ] ]";
#endif
/*
* Disable variables mapping
*/
struct DisableVariableMap {
char *name;
int flag;
} DisableVariableMap [] = {
{ MailCheckVariable, DISABLE_MAILCHECK },
{ OptArgVariable, DISABLE_OPTARG },
{ OptIndVariable, DISABLE_OPTIND },
{ SecondsVariable, DISABLE_SECONDS },
{ RandomVariable, DISABLE_RANDOM },
{ LastWordVariable, DISABLE_LASTWORD },
{ LineCountVariable, DISABLE_LINECOUNT },
{ (char *)NULL, 0 },
};
/*
* built-in commands: : and true
*/
static int dolabel (int argc, char **argv)
{
return 0;
}
static int dofalse (int argc, char **argv)
{
return 1;
}
/*
* Getopts - split arguments. getopts optstring name [ arg ... ]
*/
static int dogetopts (int argc, char **argv)
{
char **Arguments;
char *OptionString;
int result;
char SResult[3];
char BadResult[2];
int Mode = GETOPT_MESSAGE | GETOPT_PLUS;
if (argc < 3)
return UsageError ("getopts optstring name [ arg ... ]");
memset (SResult, 0, 3);
/*
* A leading : in optstring causes getopts to store the letter of an
* invalid option in OPTARG, and to set name to ? for an unknown option and
* to : when a required option is missing.
*/
if (*(OptionString = argv[1]) == ':')
{
OptionString++;
Mode = GETOPT_PLUS;
}
/*
* Use positional parameters
*/
if (argc == 3)
{
argc = ParameterCount + 1;
Arguments = ParameterArray;
}
/* No - use supplied */
else
{
Arguments = &argv[2]; /* Adjust pointers */
argc -= 2;
}
/*
* Get the value of OPTIND and initialise the getopt function
*/
if (!(DisabledVariables & DISABLE_OPTIND))
OptionIndex = (int)GetVariableAsNumeric (OptIndVariable);
else
OptionIndex = GetOptsIndex.Index;
/* Initialise the other values */
GetOptionPosition = GetOptsIndex.SubIndex;
OptionArgument = (char *)NULL;
result = GetOptions (argc, Arguments, OptionString, Mode);
/* Save new positions */
SaveGetoptsValues (OptionIndex, GetOptionPosition);
/* Check for EOF */
if (result == EOF)
return 1;
/* Set up result string */
*SResult = (char)result;
/* Did we get an error. Yes. If message output, don't put value
* in OPTARG
*/
if (result == '?')
{
if (Mode & GETOPT_MESSAGE)
OptionArgument = (char *)NULL;
/* Error, set up values in optarg and the result */
else
{
SResult[0] = (char)((OptionArgument == (char *)NULL) ? '?' : ':');
*(OptionArgument = BadResult) = (char)BadOptionValue;
*(OptionArgument + 1) = 0;
}
}
/* If the argument started with a +, tell them */
else if (OptionStart == '+')
{
SResult[1] = (char)result;
SResult[0] = '+';
}
/* If we got an argument, set the various shell variables */
if ((OptionArgument != (char *)NULL) &&
(!(DisabledVariables & DISABLE_OPTARG)))
SetVariableFromString (OptArgVariable, OptionArgument);
SetVariableFromString (argv[2], SResult);
return 0;
}
/*
* Reset the getopts values
*/
void ResetGetoptsValues (bool Variable)
{
if (Variable && (!(DisabledVariables & DISABLE_OPTIND)))
SetVariableFromNumeric (OptIndVariable, 1L);
GetOptsIndex.Index = 1;
GetOptsIndex.SubIndex = 1;
}
/*
* Save the new Getopts values
*/
void SaveGetoptsValues (int Index, int Position)
{
if (!(DisabledVariables & DISABLE_OPTIND))
SetVariableFromNumeric (OptIndVariable, (long)Index);
GetOptsIndex.Index = Index;
GetOptsIndex.SubIndex = Position;
}
/*
* Get the current Getopts values
*/
void GetGetoptsValues (GetoptsIndex *values)
{
values->Index = GetOptsIndex.Index;
values->SubIndex = GetOptsIndex.SubIndex;
}
/*
* Echo the parameters: echo [ -n ] parameters
*/
static int doecho (int argc, char **argv)
{
int flags = ECHO_ESCAPE;
FILE *fp = stdout;
char *ip; /* Input pointer */
char *op;
int c, c1;
int R_flag = GETOPT_PRINT; /* Enable -n test */
ResetGetOptions (); /* Reset GetOptions */
/* Echo or print? */
if (strcmp (*argv, LIT_print) == 0)
{
R_flag = 0; /* Reset */
while ((c = GetOptions (argc, argv, "Rnprsu:", R_flag)) != EOF)
{
switch (c)
{
case 'R':
R_flag = GETOPT_PRINT;
flags &= ~ECHO_ESCAPE;
break;
case 'n':
flags = ECHO_NO_EOL;
break;
case 'r':
flags &= ~ECHO_ESCAPE;
break;
case 's':
flags |= ECHO_HISTORY;
break;
case 'p':
break;
case 'u':
if ((c = GetUnitNumber (LIT_print)) == -1)
return 1;
if ((fp = fdopen (c, "wt")) == (FILE *)NULL)
return PrintWarningMessage (LIT_Emsg, LIT_bun,
LIT_print, OptionArgument);
break;
default:
return UsageError ("print [ -Rpnrsu[unit] ] ...");
}
}
}
if ((OptionIndex < argc) && (R_flag == GETOPT_PRINT) &&
(!strcmp (argv[OptionIndex], "-n")))
{
flags |= ECHO_NO_EOL;
++OptionIndex;
}
argv += OptionIndex;
/* Clear the history buffer so we can use it */
FlushHistoryBuffer ();
op = ConsoleLineBuffer;
/* Process the arguments. Process \ as a special if necessary */
while ((ip = *(argv++)) != (char *)NULL)
{
/* If echo too big - disable history save */
if ((op - ConsoleLineBuffer) > LINE_MAX - 4)
{
*op = 0;
fputs (op = ConsoleLineBuffer, fp);
if (flags & ECHO_HISTORY)
fprintf (stderr, BasicErrorMessage, "Line too long for history",
LIT_print);
flags &= ~ECHO_HISTORY;
}
/* Process the character */
while (c = *(ip++))
{
if ((flags & ECHO_ESCAPE) && (c == '\\'))
{
c1 = *ip;
if ((c = ProcessOutputMetaCharacters (&ip)) == -1)
{
flags |= ECHO_NO_EOL;
continue;
}
/* If unchanged - output backslash */
else if ((c == c1) && (c != '\\'))
*(op++) = '\\';
}
*(op++) = (char)c;
}
/* End of string - check to see if a space if required */
if (*argv != (char *)NULL)
*(op++) = CHAR_SPACE;
}
/* Is EOL required ? */
if (!(flags & ECHO_NO_EOL))
*(op++) = CHAR_NEW_LINE;
*op = 0;
fputs (ConsoleLineBuffer, fp);
fflush (fp);
if (fp != stdout)
{
fileno (fp) = -1;
fclose (fp);
}
/* Save history */
if (flags & ECHO_HISTORY)
{
CleanUpBuffer (op - ConsoleLineBuffer, ConsoleLineBuffer, 0x1a);
AddHistory (FALSE);
}
return 0;
}
/*
* ProcessOutputMetaCharacters - Convert an escaped character to a binary value.
*
* Returns the binary value and updates the string pointer.
*/
static struct {
char Escaped;
int NewValue;
} ConvertMetaCharacters [] =
{
{ 'b', 0x08}, { 'f', 0x0c}, { 'v', 0x0b}, { 'n', 0x0a},
{ 'r', 0x0d}, { 't', 0x09}, { '\\', '\\'}, { 'c', -1},
{ 0, 0}
};
int ProcessOutputMetaCharacters (char **cp)
{
int c_val = **cp; /* Current character */
int j = 0;
if (c_val)
(*cp)++;
/* Process escaped characters */
while (ConvertMetaCharacters[j].Escaped != 0)
{
if (ConvertMetaCharacters[j].Escaped == (char)c_val)
return ConvertMetaCharacters[j].NewValue;
++j;
}
/* Check for an octal string */
if (IS_OCTAL (c_val))
{
c_val -= '0';
while ((IS_OCTAL (**cp)))
c_val = (c_val * 8) + *((*cp)++) - '0';
return c_val;
}
return c_val;
}
/*
* Display the current version: ver
*/
static int dover (int argc, char **argv)
{
PrintVersionNumber (stdout);
return 0;
}
#ifndef OS2
static char *swap_device[] = {"disk", "extend", "expand"};
#endif
/*
* Modify swapping information: swap [ options ]
*/
static int doswap (int argc, char **argv)
{
#ifdef OS2
puts ("Swapping not available on OS/2");
#else
register int n = 1;
char *cp;
/* Display current values ? */
if (argv[1] == (char *)NULL)
{
if (Swap_Mode == SWAP_OFF)
puts ("Swapping disabled");
else
{
register int j;
fputs ("Swap devices: ", stdout);
for (j = 0, n = 1; j < 3; ++j, n <<= 1)
{
if (Swap_Mode & n)
{
printf ("%s ", swap_device[j]);
if (n == SWAP_EXTEND)
printf ("(0x%.6lx) ", SW_EMstart);
}
}
putchar (CHAR_NEW_LINE);
}
return 0;
}
/* Set up new values */
Swap_Mode = SWAP_OFF;
ClearSwapFile ();
while ((cp = argv[n++]) != (char *)NULL)
{
if (strcmp (cp, "off") == 0)
Swap_Mode = SWAP_OFF;
else if (strcmp (cp, "on") == 0)
Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND;
/* Scan for valid arguments */
else
{
register int j, k;
for (j = 0, k = 1; j < 3; ++j, k <<= 1)
{
if (strcmp (cp, swap_device[j]) == 0)
{
Swap_Mode |= k;
/* If extended memory, they can specify the start address as a hex number */
if (k == SWAP_EXTEND)
{
char *sp;
long start;
/* Check for not changed */
if ((sp = argv[n]) == (char *)NULL)
break;
/* Convert hex number */
if (!ConvertNumericValue (sp, &start, 16))
break;
/* Set used and saved new value */
SW_EMstart = start;
++n;
if ((SW_EMstart < 0x100000L) ||
(SW_EMstart > 0xf00000L))
SW_EMstart = 0x100000L;
printf ("Extend memory start set to 0x%.6lx\n",
SW_EMstart);
}
break;
}
}
if (j == 3)
return UsageError ("swap [ on | off | disk | expand | extend [ address ] ] ...");
}
}
#endif
return 0;
}
/*
* Output the current path: pwd [drives]
*/
static int dopwd (int argc, char **argv)
{
int i = 1;
int RetVal = 0;
char *sp;
unsigned int ndrive;
char ldir[PATH_MAX + 6];
/* Print the current directories on the selected drive */
while ((sp = argv[i++]) != (char *)NULL)
{
/* Select the drive and get the path */
while (ndrive = (unsigned int)(*(sp++)))
{
errno = 0;
_getdcwd (tolower (ndrive) - 'a' + 1, ldir, PATH_MAX + 4);
ldir[PATH_MAX + 5] = 0;
if (errno)
RetVal = PrintWarningMessage (BadDrive, ndrive);
else
{
#ifdef OS2
if (!IsHPFSFileSystem (ldir))
#endif
strlwr (ldir);
puts (PATH_TO_UNIX (ldir));
}
}
}
/* Print the current directory */
if (argv[1] == (char *)NULL)
puts (CurrentDirectory->value);
return RetVal;
}
/*
* Unset a variable: unset [ flag ] [ variable name... ]
* Delete a function: unfunction <names ...>
*/
static int dounset (int argc, char **argv)
{
register int n = 1;
register bool Fnc = FALSE;
FunctionList *fp;
char *cp;
int i;
/* -f, functions */
if (strcmp (*argv, LIT_unfunction) == 0)
Fnc = TRUE;
else if ((argc > 1) && (strcmp (argv[1], "-f") == 0))
{
n++;
Fnc = TRUE;
}
/* Unset the variables, flags or functions */
while ((cp = argv[n++]) != (char *)NULL)
{
if (!Fnc)
{
UnSetVariable (cp, FALSE);
for (i = 0; DisableVariableMap[i].name != (char *)NULL; i++)
{
if (strcmp (DisableVariableMap[i].name, cp) == 0)
{
DisabledVariables |= DisableVariableMap[i].flag;
break;
}
}
}
else if ((fp = LookUpFunction (cp)) != (FunctionList *)NULL)
DeleteFunction (fp->tree);
}
return 0;
}
/* Delete a variable. If all is set, system variables can be deleted.
* This is used to delete the trap functions
*/
void UnSetVariable (register char *cp, bool all)
{
VariableList **vpp;
VariableList *vp;
void (*save_signal)(int);
/* Unset a flag */
if (*cp == '-')
{
while (*(++cp) != 0)
{
if (islower (*cp))
FL_CLEAR (*cp);
}
SetShellSwitches ();
return;
}
/* Ok - unset a variable and not a local value */
if (!all && !(isalpha (*cp)))
return;
/* Check in list */
vpp = (VariableList **)tfind (cp, &VariableTree, FindVariable);
/* If not found, Ignore unset request */
if (vpp == (VariableList **)NULL)
return;
/* Error if read-only */
vp = *vpp;
if (vp->status & (STATUS_READONLY | STATUS_CANNOT_UNSET))
{
PrintWarningMessage ("unset: %s %s\n", vp->name,
(vp->status & STATUS_CANNOT_UNSET)
? "cannot be unset" : "is read-only");
return;
}
/* Disable signals */
save_signal = signal (SIGINT, SIG_IGN);
/* Delete it */
tdelete (cp, &VariableTree, FindVariable);
ReleaseMemoryCell ((void *)vp->name);
if (vp->value != null)
ReleaseMemoryCell ((void *)vp->value);
ReleaseMemoryCell ((void *)vp);
/* Restore signals */
signal (SIGINT, save_signal);
}
/*
* Execute a test: test <arguments>
*/
static int dotest (int argc, char **argv)
{
int st = 0;
char *End;
NewTestProgram = (bool)(strcmp (TestProgram = *argv, "[[") == 0);
/* Note that -a and -o change meaning if [[ ... ]] is used */
if (NewTestProgram)
{
End = "]]";
ListOfTestOperators[0].OperatorID = FILE_EXISTS;
ListOfTestOperators[0].OperatorType = UNARY_OP;
ListOfTestOperators[1].OperatorID = TEST_OPTION;
ListOfTestOperators[1].OperatorType = UNARY_OP;
}
else
{
End = "]";
ListOfTestOperators[0].OperatorID = BINARY_AND;
ListOfTestOperators[0].OperatorType = B_BINARY_OP;
ListOfTestOperators[1].OperatorID = BINARY_OR;
ListOfTestOperators[1].OperatorType = B_BINARY_OP;
}
/* Check out */
CurrentTestOperator = (struct TestOperator *)NULL;
/* If [ <arguments> ] or [[ <arguments> ]] form, check for end ] or ]] and
* remove it
*/
if (NewTestProgram || (strcmp (*argv, "[") == 0))
{
while (argv[++st] != (char *)NULL)
;
if (strcmp (argv[--st], End) != 0)
return PrintWarningMessage ("%s: missing '%s'\n", TestProgram, End);
else
argv[st] = (char *)NULL;
}
/* Check for null expression */
if (*(TestArgumentList = &argv[1]) == (char *)NULL)
return 1;
/* Set abort address */
if (setjmp (TestErrorReturn))
return 1;
st = !TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList));
if (*(TestArgumentList + 1) != (char *)NULL)
TestSyntaxError ();
return (st);
}
/*
* Process next test expression
*/
static int near TestProcessNextExpression (int n)
{
int res;
if (n == END_OF_INPUT)
TestSyntaxError ();
res = TestANDExpression (n);
TestArgumentList++;
if (TestLexicalAnalysis (*TestArgumentList) == BINARY_OR)
{
TestArgumentList++;
return TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList)) || res;
}
TestArgumentList--;
return res;
}
/*
* Binary expression ( a AND b)
*/
static int near TestANDExpression (int n)
{
int res;
if (n == END_OF_INPUT)
TestSyntaxError ();
res = TestPrimaryExpression (n);
TestArgumentList++;
if (TestLexicalAnalysis (*TestArgumentList) == BINARY_AND)
{
TestArgumentList++;
return TestANDExpression (TestLexicalAnalysis (*TestArgumentList)) && res;
}
TestArgumentList--;
return res;
}
/*
* Handle Primary expression
*/
static int near TestPrimaryExpression (int n)
{
register char *opnd1, *opnd2;
struct stat s, s1;
int res;
if (n == END_OF_INPUT)
TestSyntaxError ();
if (n == UNARY_NOT)
{
TestArgumentList++;
return !TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList));
}
if (n == LPAREN)
{
TestArgumentList++;
res = TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList));
TestArgumentList++;
if (TestLexicalAnalysis (*TestArgumentList) != RPAREN)
TestSyntaxError ();
return res;
}
/* Operand */
if (n == OPERAND)
{
opnd1 = *(TestArgumentList++);
(void) TestLexicalAnalysis (*TestArgumentList);
if ((CurrentTestOperator != (struct TestOperator *)NULL) &&
(CurrentTestOperator->OperatorType == BINARY_OP))
{
struct TestOperator *op = CurrentTestOperator;
TestArgumentList++;
if ((opnd2 = *TestArgumentList) == (char *)NULL)
TestSyntaxError ();
switch (op->OperatorID)
{
/* String lengths */
case STRING_EQUAL:
return strcmp (opnd1, opnd2) == 0;
case STRING_NOTEQUAL:
return strcmp (opnd1, opnd2) != 0;
case STRING_LESS:
return strcmp (opnd1, opnd2) < 0;
case STRING_GREATER:
return strcmp (opnd1, opnd2) > 0;
/* Numeric comparisions */
case NUMBER_EQUAL:
return GetNumberForTest (opnd1) == GetNumberForTest (opnd2);
case NUMBER_NOTEQUAL:
return GetNumberForTest (opnd1) != GetNumberForTest (opnd2);
case NUMBER_EQ_GREAT:
return GetNumberForTest (opnd1) >= GetNumberForTest (opnd2);
case NUMBER_GREATER:
return GetNumberForTest (opnd1) > GetNumberForTest (opnd2);
case NUMBER_EQ_LESS:
return GetNumberForTest (opnd1) <= GetNumberForTest (opnd2);
case NUMBER_LESS:
return GetNumberForTest (opnd1) < GetNumberForTest (opnd2);
/* Older and Newer - if file not found - set to current time */
case FILE_NEWER:
case FILE_OLDER:
if (stat (CheckDOSFileName (opnd1), &s) == -1)
return 0;
if (stat (CheckDOSFileName (opnd2), &s1) == -1)
s1.st_mtime = time ((time_t *)NULL);
return (op->OperatorID == FILE_NEWER)
? (s.st_mtime > s1.st_mtime)
: (s.st_mtime < s1.st_mtime);
/* Equals - difficult on DOS. So just do want UNIX says */
case FILE_EQUAL:
if ((stat (CheckDOSFileName (opnd1), &s) == -1) ||
(stat (CheckDOSFileName (opnd2), &s1) == -1))
return 0;
return ((s.st_dev == s1.st_dev) && (s.st_ino < s1.st_ino));
}
}
TestArgumentList--;
return strlen (opnd1) > 0;
}
/* unary expression */
if ((CurrentTestOperator->OperatorType != UNARY_OP) ||
(*++TestArgumentList == 0))
TestSyntaxError ();
switch (n)
{
case STRING_ZERO:
return strlen (*TestArgumentList) == 0;
case STRING_NONZERO:
return strlen (*TestArgumentList) != 0;
case TEST_OPTION:
return TestOptionValue (*TestArgumentList) != 0;
/* File functions */
case FILE_EXISTS:
return access (CheckDOSFileName (*TestArgumentList), F_OK) == 0;
case FILE_READABLE:
return access (CheckDOSFileName (*TestArgumentList), R_OK) == 0;
case FILE_WRITABLE:
return access (CheckDOSFileName (*TestArgumentList), W_OK) == 0;
case FILE_EXECUTABLE:
return access (CheckDOSFileName (*TestArgumentList), X_OK) == 0;
case FILE_REGULAR:
return stat (CheckDOSFileName (*TestArgumentList), &s) == 0 &&
S_ISREG(s.st_mode);
case FILE_DIRECTRY:
return stat (CheckDOSFileName (*TestArgumentList), &s) == 0 &&
S_ISDIR(s.st_mode);
case FILE_NONZERO:
return stat (CheckDOSFileName (*TestArgumentList), &s) == 0 &&
(s.st_size > 0L);
case FILE_TERMINAL:
return isatty ((int)GetNumberForTest (*TestArgumentList));
/* The following have no meaning on MSDOS or OS/2. So we always return
* fail for compatability
*/
case FILE_USER:
case FILE_GROUP:
case FILE_TEXT:
case FILE_BLOCK:
case FILE_CHARACTER:
case FILE_FIFO:
case FILE_SYMBOLIC:
case FILE_SOCKET:
return 0;
/* Under MSDOS and OS/2, we always own the file. Not necessarily true on
* networked versions. But there is no common way of finding out
*/
case FILE_OWNER:
case FILE_GROUPER:
return 1;
}
}
/* Operator or Operand ? */
static int near TestLexicalAnalysis (register char *s)
{
register struct TestOperator *op = ListOfTestOperators;
if (s == (char *)NULL)
return END_OF_INPUT;
while (op->OperatorName)
{
if (strcmp (s, op->OperatorName) == 0)
{
CurrentTestOperator = op;
return op->OperatorID;
}
op++;
}
CurrentTestOperator = (struct TestOperator *)NULL;
return OPERAND;
}
/*
* Get a long numeric value
*/
static long near GetNumberForTest (register char *s)
{
long l;
if (!ConvertNumericValue (s, &l, 10))
TestSyntaxError ();
return l;
}
/*
* test syntax error - abort
*/
static void near TestSyntaxError (void)
{
PrintWarningMessage (BasicErrorMessage, TestProgram, "syntax error");
longjmp (TestErrorReturn, 1);
}
/*
* Select a new drive: x:
*
* Select the drive, get the current directory and check that we have
* actually selected the drive
*/
static int dodrive (int argc, char **argv)
{
unsigned int ndrive = tolower (**argv) - 'a' + 1;
if (argc != 1)
return UsageError ("drive:");
SetCurrentDrive (ndrive);
GetCurrentDirectory ();
if (ndrive != GetCurrentDrive ())
return PrintWarningMessage (BadDrive, **argv);
else
return 0;
}
/*
* Select a new directory:
*
* cd [ directory ] Select new directory
* cd - Select previous directory
* cd <search string> <new string> Select directory based on current
* cd Select Home directory
*/
static int dochdir (int argc, char **argv)
{
char *NewDirectory; /* Original new directory */
char *CNDirectory; /* New directory */
register char *cp; /* In CDPATH Pointer */
char *directory;
int first = 0;
unsigned int Length;
unsigned int cdrive;
/* If restricted shell - illegal */
if (CheckForRestrictedShell ("cd"))
return 1;
if (argc > 3)
return UsageError ("cd [ directory | - | [ old string ] [ new string]");
/* Use default ? */
if (((NewDirectory = argv[1]) == (char *)NULL) &&
((NewDirectory = GetVariableAsString (HomeVariableName,
FALSE)) == null))
return PrintWarningMessage ("cd: no home directory\n");
if ((directory = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
return doOutofMemory ("cd");
if ((strcmp (NewDirectory, ShellOptionsVariable) == 0) &&
((NewDirectory = GetVariableAsString (OldPWDVariable, FALSE)) == null))
return PrintWarningMessage ("cd: no previous directory\n");
/* Check for substitue */
if ((argv[1] != (char *)NULL) && (argv[2] != (char *)NULL))
{
if ((cp = strstr (CurrentDirectory->value, argv[1])) == (char *)NULL)
return PrintWarningMessage ("cd: string not in pwd: %s", argv[1]);
if (strlen (CurrentDirectory->value) - strlen (argv[1]) +
strlen (argv[2]) >= FFNAME_MAX)
return PrintWarningMessage ("cd: new directory string too long: %s",
argv[1]);
/* Do the substitution */
Length = cp - CurrentDirectory->value;
strncpy (NewDirectory, CurrentDirectory->value, Length);
strcpy (NewDirectory + Length, argv[2]);
strcat (NewDirectory,
CurrentDirectory->value + strlen (argv[1]) + Length);
}
/* Save the current drive */
cdrive = GetCurrentDrive ();
/* Remove trailing / */
Length = strlen (NewDirectory) - 1;
if ((NewDirectory[Length] == CHAR_UNIX_DIRECTORY) &&
(!((!Length) || ((Length == 2) && (NewDirectory[1] == ':')))))
NewDirectory[Length] = 0;
/* Scan for the directory. If there is not a / or : at start, use the
* CDPATH variable
*/
cp = ((*NewDirectory == CHAR_UNIX_DIRECTORY) ||
(*(NewDirectory + 1) == ':'))
? null : GetVariableAsString ("CDPATH", FALSE);
do
{
cp = BuildNextFullPathName (cp, NewDirectory, directory);
/* Check for new disk drive */
CNDirectory = directory;
if (*(CNDirectory + 1) == ':')
{
SetCurrentDrive (tolower (*CNDirectory) - 'a' + 1);
CNDirectory += 2;
}
/* Was the change successful? */
if ((!*CNDirectory) || (chdir (CheckDOSFileName (CNDirectory)) == 0))
{
/* OK - reset the current directory (in the shell) and display the new
* path if appropriate
*/
GetCurrentDirectory ();
if (first)
puts (CurrentDirectory->value);
return 0;
}
first = 1;
} while (cp != (char *)NULL);
/* Restore our original drive and restore directory info */
SetCurrentDrive (cdrive);
GetCurrentDirectory ();
return PrintWarningMessage (BasicErrorMessage, NewDirectory,
"bad directory");
}
/*
* Extract the next path from a string and build a new path from the
* extracted path and a file name
*
* path_s - Path string
* file_s - File name string
* output_s - Output path
*/
char *BuildNextFullPathName (register char *path_s, register char *file_s,
char *output_s)
{
register char *s = output_s;
int fsize = 0;
while (*path_s && (*path_s != ';') && (fsize++ < FFNAME_MAX))
*s++ = *path_s++;
if ((output_s != s) && (*(s - 1) != CHAR_UNIX_DIRECTORY) &&
(fsize++ < FFNAME_MAX))
*s++ = CHAR_UNIX_DIRECTORY;
*s = '\0';
if (file_s != (char *)NULL)
strncpy (s, file_s, FFNAME_MAX - fsize);
output_s[FFNAME_MAX - 1] = 0;
return (*path_s ? ++path_s : (char *)NULL);
}
/*
* Execute a shift command: shift [ n ]
*/
static int doshift (int argc, char **argv)
{
register int n;
char *Nvalue = argv[1];
if (argc > 2)
return UsageError ("shift [ count ]");
n = (Nvalue != (char *)NULL) ? GetNumericValue (Nvalue) : 1;
if (n < 0)
return PrintWarningMessage (LIT_Emsg, LIT_shift, "bad shift value",
Nvalue);
if (ParameterCount < n)
return PrintWarningMessage (BasicErrorMessage, LIT_shift,
"nothing to shift");
return SetUpNewParameterVariables (ParameterArray, n + 1,
ParameterCount + 1, LIT_shift);
}
/*
* Execute a umask command: umask [ n ]
*/
static int doumask (int argc, char **argv)
{
register int i;
register char *cp;
long value;
if (argc > 2)
return UsageError ("umask [ new mask ]");
if ((cp = argv[1]) == (char *)NULL)
{
umask ((i = umask (0)));
printf ("%o\n", i);
}
else
{
if (!ConvertNumericValue (cp, &value, 8))
return PrintWarningMessage ("umask: bad mask (%s)\n", cp);
umask ((int)value);
}
return 0;
}
/*
* Execute an exec command: exec [ arguments ]
*/
int doexec (C_Op *t)
{
register int i;
jmp_buf ex;
int *ofail;
/* Shift arguments */
for (i = 0; (t->words[i] = t->words[i + 1]) != (char *)NULL; i++)
;
/* Left the I/O as it is */
if (i == 0)
return RestoreStandardIO (0, FALSE);
ProcessingEXECCommand = TRUE;
ofail = FailReturnPoint;
/* Set execute function recursive level to zero */
Execute_stack_depth = 0;
t->ioact = (IO_Actions **)NULL;
if (setjmp (FailReturnPoint = ex) == 0)
ExecuteParseTree (t, NOPIPE, NOPIPE, EXEC_WITHOUT_FORK);
/* Clear the extended file if an interrupt happened */
ClearExtendedLineFile ();
FailReturnPoint = ofail;
ProcessingEXECCommand = FALSE;
return 1;
}
/*
* Execute a script in the current shell: . <filename>
*/
static int dodot (int argc, char **argv)
{
register int i;
register char *sp;
char *cp;
char *l_path;
if ((cp = argv[1]) == (char *)NULL)
return 0;
/* Get some space */
if ((l_path = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
return doOutofMemory (".");
/* Save the current drive */
sp = (any (CHAR_UNIX_DIRECTORY, cp) || (*(cp + 1) == ':'))
? null : GetVariableAsString (PathLiteral, FALSE);
do
{
sp = BuildNextFullPathName (sp, cp, l_path);
if ((i = OpenForExecution (l_path, (char **)NULL, (int *)NULL)) >= 0)
return RUN (afile, ReMapIOHandler (i), File_GetNextCharacter,
FALSE, l_path, &argv[1]);
} while (sp != (char *)NULL);
return PrintWarningMessage (BasicErrorMessage, cp, NotFound);
}
/*
* Read from standard input into a variable list
*
* read [-prs] [-u unit] [ variable list ]
*/
static int doread (int argc, char **argv)
{
register char *cp, *op;
register int i;
int Unit = STDIN_FILENO;
bool EndOfInputDetected = FALSE;
int PreviousCharacter = 0;
bool SaveMode = FALSE;
bool RawMode = FALSE;
char *Prompt = (char *)NULL;
char *NewBuffer;
int eofc;
if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 1)) == (char *)NULL)
return doOutofMemory (LIT_read);
/* Check for variable name. If not defined, use $REPLY */
ResetGetOptions (); /* Reset GetOptions */
while ((i = GetOptions (argc, argv, "prsu:", 0)) != EOF)
{
switch (i)
{
case 'p': /* Clean up process */
break;
case 'r': /* Raw Mode */
RawMode = TRUE;
break;
case 's': /* Save a command */
SaveMode = TRUE;
break;
case 'u': /* Specify input unit */
if ((Unit = GetUnitNumber (LIT_read)) == -1)
return 2;
default:
return UsageError ("read [ -prs ] [ -u unit ] [ name?prompt ] [ name ... ]");
}
}
argv += OptionIndex;
argc -= OptionIndex;
if (!argc)
argv = Reply_Array;
/* Get the prompt and write it */
LastUserPrompt = null;
if ((Prompt = strchr (argv[0], '?')) != (char *)NULL)
{
*(Prompt++) = 0;
LastUserPrompt1 = Prompt;
LastUserPrompt = (char *)NULL;
}
/* Clear the history buffer */
FlushHistoryBuffer ();
/* Read the line from the device */
if (ReadALine (Unit, Prompt, SaveMode, FALSE, &eofc))
return 1;
cp = ConsoleLineBuffer;
/* For each variable, read the data until a white space is detected */
while (*argv != (char *)NULL)
{
/* Read in until end of line, file or a field separator is detected */
op = (char *)NULL;
while (!EndOfInputDetected && *cp)
{
/* End of file */
if (*cp == (char)eofc)
return 1;
/* Check for Newline or IFS character */
if ((*cp == CHAR_NEW_LINE) ||
((argv[1] != (char *)NULL) &&
any (*cp, GetVariableAsString (IFS, FALSE))))
{
if (*cp != CHAR_NEW_LINE)
;
else if ((PreviousCharacter != '\\') || (RawMode))
EndOfInputDetected = TRUE;
/* Handle continuation line */
else if (ReadALine (Unit, Prompt, SaveMode, TRUE, &eofc))
return 1;
else
{
cp = ConsoleLineBuffer;
PreviousCharacter = 0;
continue;
}
break;
}
/* Save the current character */
if (op == (char *)NULL)
op = NewBuffer;
*(op++) = *cp;
PreviousCharacter = *(cp++);
}
/* Skip over terminating character */
if (*cp)
++cp;
/* Check for null string */
if (op == (char *)NULL)
op = null;
/* Terminate the string */
else
{
*op = 0;
if (!strlen (NewBuffer))
continue;
else
op = NewBuffer;
}
/* Save the string value */
SetVariableFromString (*(argv++), op);
}
ReleaseMemoryCell ((void *)NewBuffer);
return 0;
}
/*
* Read a line from either the console or a file for the read command.
* The line is returned in the ConsoleLineBuffer.
*/
static bool near ReadALine (int Unit, char *Prompt, bool SaveMode,
bool Append, int *eofc)
{
int NumberBytesRead;
char *cp;
int x;
/* Generate the prompt */
if ((Prompt != (char *)NULL) && (!IsConsole (Unit)) &&
(!isatty (Unit) || (write (Unit, Prompt, strlen (Prompt)) == -1)))
fputs (Prompt, stderr);
/* Read the line */
*eofc = 0x1a;
if (IsConsole (Unit))
{
*eofc = GetEOFKey ();
GetLineFromConsole (); /* No - get input */
if (((x = strlen(ConsoleLineBuffer)) != 1) &&
(ConsoleLineBuffer[x] == (char)*eofc))
ConsoleLineBuffer[x] = 0;
strcat (ConsoleLineBuffer, "\n");
}
else
{
NumberBytesRead = 0;
cp = ConsoleLineBuffer;
while (NumberBytesRead++ < LINE_MAX)
{
if ((x = read (Unit, cp, 1)) == -1)
return TRUE;
/* EOF detected as first character on line */
if ((NumberBytesRead == 1) && ((!x) || (*cp == (char)*eofc)))
return TRUE;
/* End read if NEWLINE or EOF character detected */
if ((!x) || (*cp == CHAR_NEW_LINE) || (*cp == (char)*eofc))
break;
cp++;
}
/* Terminate the line the same in all cases */
*(cp++) = CHAR_NEW_LINE;
*cp = 0;
}
/* Save the history. Clean it up first */
if (SaveMode)
{
char save;
save = CleanUpBuffer (strlen (ConsoleLineBuffer), ConsoleLineBuffer,
*eofc);
AddHistory (Append);
ConsoleLineBuffer[strlen(ConsoleLineBuffer)] = save;
}
return FALSE;
}
/*
* Evaluate an expression: eval <expression>
*/
static int doeval (int argc, char **argv)
{
return RUN (awordlist, argv + 1, WordList_GetNextCharacter, TRUE,
null, (char **)NULL);
}
/*
* Execute a trap: trap [ number... ] [ command ]
*/
static int dotrap (int argc, char **argv)
{
register int n, i;
register bool ResetSignal;
char tval[10];
char *cp;
if (argc < 2)
{
/* Display trap - look up each trap and print those we find */
for (i = 0; i < NSIG; i++)
{
*tval = '~';
itoa (i, tval + 1, 10);
if ((cp = GetVariableAsString (tval, FALSE)) != null)
printf ("%u: %s\n", i, cp);
}
if ((cp = GetVariableAsString (Trap_DEBUG, FALSE)) != null)
printf (BasicErrorMessage, &Trap_DEBUG[1], cp);
if ((cp = GetVariableAsString (Trap_ERR, FALSE)) != null)
printf (BasicErrorMessage, &Trap_ERR[1], cp);
return 0;
}
/* Check to see if signal re-set */
ResetSignal = (bool)(isdigit (*argv[1]) ||
(!stricmp (argv[1], LIT_exit)) ||
(!stricmp (argv[1], &Trap_ERR[1])) ||
(!stricmp (argv[1], &Trap_DEBUG[1])));
for (i = ResetSignal ? 1 : 2; argv[i] != (char *)NULL; ++i)
{
n = 0; /* Set not a real signal */
/* Check for ERR. cp points to the variable name */
if (!stricmp (argv[i], &Trap_ERR[1]))
cp = Trap_ERR;
/* Check for DEBUG */
else if (!stricmp (argv[i], &Trap_DEBUG[1]))
cp = Trap_DEBUG;
/* Check for EXIT */
else if (!stricmp (argv[i], LIT_exit))
cp = "~0";
/* Generate the variable name for a numeric value */
else
{
*(cp = tval) = '~';
itoa ((n = GetSignalNumber (argv[i])), tval + 1, 10);
if (n == -1)
return 1;
}
UnSetVariable (cp, TRUE);
/* Re-define signal processing */
if (!ResetSignal)
{
if (*argv[1] != '\0')
{
SetVariableFromString (cp, argv[1]);
if (n > 0)
signal (n, TerminateSignalled);
}
else if (n > 0)
signal (n, SIG_IGN);
}
/* Clear signal processing */
else if (InteractiveFlag)
{
if (n == SIGINT)
signal (n, InterruptSignalled);
else if (n > 0)
#ifdef SIGQUIT
signal (n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
#else
signal (n, SIG_DFL);
#endif
}
else if (n > 0)
signal (n, SIG_DFL);
}
return 0;
}
/*
* Get a signal number
*/
static int near GetSignalNumber (char *s)
{
register int n;
if (((n = GetNumericValue (s)) < 0) || (n >= NSIG))
{
PrintWarningMessage ("trap: bad signal number\n");
n = -1;
}
return n;
}
/* Convert a string to a number */
int GetNumericValue (char *as)
{
long value;
return ConvertNumericValue (as, &value, 10) ? (int) value : -1;
}
/*
* BREAK and CONTINUE processing: break/continue [ n ]
*/
static int dobreak (int argc, char **argv)
{
if (argc > 2)
return UsageError ("break [ count ]");
return BreakContinueProcessing (argv[1], BC_BREAK);
}
static int docontinue (int argc, char **argv)
{
if (argc > 2)
return UsageError ("continue [ count ]");
return BreakContinueProcessing (argv[1], BC_CONTINUE);
}
static int near BreakContinueProcessing (register char *NumberOfLevels,
int Type)
{
register Break_C *Break_Loc;
register int LevelNumber;
char *cType = (Type == BC_BREAK) ? LIT_break : LIT_continue;
LevelNumber = (NumberOfLevels == (char *)NULL)
? 1 : GetNumericValue (NumberOfLevels);
if (LevelNumber < 0)
return PrintWarningMessage (LIT_Emsg, "bad level number", cType,
NumberOfLevels);
/* If level is invalid - clear all levels */
if (LevelNumber <= 0)
LevelNumber = 999;
/* Move down the stack */
do
{
if ((Break_Loc = Break_List) == (Break_C *)NULL)
break;
Break_List = Break_Loc->NextExitLevel;
} while (--LevelNumber);
/* Check level */
if (LevelNumber)
return PrintWarningMessage ("sh: bad break/continue level\n");
longjmp (Break_Loc->CurrentReturnPoint, Type);
/* NOTREACHED */
}
/*
* Exit function: exit [ status ]
*/
static int doexit (int argc, char **argv)
{
Break_C *SShell_Loc = SShell_List;
ProcessingEXECCommand = FALSE;
if (argc > 2)
return UsageError ("exit [ status ]");
/* Set up error codes */
ExitStatus = (argv[1] != (char *)NULL) ? GetNumericValue (argv[1]) : 0;
SetVariableFromNumeric (StatusVariable, (long)ExitStatus);
/* Are we in a subshell. Yes - do a longjmp instead of an exit */
if (SShell_Loc != (Break_C *)NULL)
{
SShell_List = SShell_Loc->NextExitLevel;
longjmp (SShell_Loc->CurrentReturnPoint, ExitStatus);
}
/*
* Check for active jobs
*/
#ifdef OS2
if (!ExitWithJobsActive)
{
if (NumberOfActiveJobs () && Interactive)
{
fputs ("You have running jobs.\n", stderr);
ExitWithJobsActive = TRUE;
return 0;
}
}
#endif
ExitTheShell (FALSE);
return ExitStatus;
}
/*
* Function return: return [ status ]
*
* Set exit value and return via a long jmp
*/
static int doreturn (int argc, char **argv)
{
Break_C *Return_Loc = Return_List;
if (argc > 2)
return UsageError ("return [ value ]");
if (argv[1] != (char *)NULL)
SetVariableFromString (StatusVariable, argv[1]);
/* If the return address is defined - return to it. Otherwise, return
* the value
*/
if (Return_Loc != (Break_C *)NULL)
{
Return_List = Return_Loc->NextExitLevel;
longjmp (Return_Loc->CurrentReturnPoint, 1);
}
return GetNumericValue (argv[1]);
}
/*
* Set function: set [ -/+flags ] [ parameters... ]
*/
static int doset (int argc, char **argv)
{
int i;
/* Display ? */
if (argc < 2)
return ListAllVariables (0xffff, TRUE);
/* Set/Unset a flag ? */
ResetGetOptions (); /* Reset GetOptions */
while ((i = GetOptions (argc, argv, "abcdefghijklmno:pqrstuvwxyz",
GETOPT_PLUS)) != EOF)
{
switch (i)
{
case '?': /* Unknown */
return UsageError ("set [ [-|+][switches] ] [ [-|+]o option ] [ parameter=value ] ...");
case 'r':
return PrintWarningMessage ("set: r switch cannot be changed\n");
case 'o':
if ((!ChangeInitialisationValue (OptionArgument,
OptionStart == '-')) &&
(!ChangeOptionValue (OptionArgument,
(bool)(OptionStart == '-'))))
return PrintWarningMessage ("set: -o bad option (%s)\n",
OptionArgument);
break;
default:
if ((i == 'e') && InteractiveFlag)
continue;
SetClearFlag (i, (bool)(OptionStart == '-'));
}
}
/* Check for --, ++, - and +, which we skip */
if ((OptionIndex != argc) &&
((!strcmp (argv[OptionIndex], DoubleHypen)) ||
(!strcmp (argv[OptionIndex], DoublePlus)) ||
(!strcmp (argv[OptionIndex], ShellOptionsVariable)) ||
(!strcmp (argv[OptionIndex], "+"))))
OptionIndex++;
/* Set up parameters ? */
if (OptionIndex != argc)
{
ResetGetoptsValues (TRUE);
return SetUpNewParameterVariables (argv, OptionIndex, argc, "set");
}
else
return 0;
}
/*
* Print the list of functions: functions [ names ]
*/
static int dofunctions (int argc, char **argv)
{
FunctionList *fp;
int i;
if (argc < 2)
return PrintAllFunctions ();
for (i = 1; argv[i] != (char *)NULL; ++i)
{
if ((fp = LookUpFunction (argv[i])) != (FunctionList *)NULL)
PrintFunction (fp->tree);
else
PrintWarningMessage ("functions: %s is not a function\n", argv[i]);
}
return 0;
}
/*
* History functions - history [-ieds]
*/
#ifndef NO_HISTORY
static int dohistory (int argc, char **argv)
{
int i;
if (!InteractiveFlag)
return 1;
if (argc < 2)
PrintHistory (FALSE, TRUE, 0, GetLastHistoryEvent () + 1, stdout);
else
{
ResetGetOptions (); /* Reset GetOptions */
while ((i = GetOptions (argc, argv, "sidel", 0)) != EOF)
{
switch (i)
{
case 's':
DumpHistory ();
break;
case 'i':
ClearHistory ();
break;
case 'l':
LoadHistory ();
break;
case 'd':
HistoryEnabled = FALSE;
break;
case 'e':
HistoryEnabled = TRUE;
break;
default:
return UsageError ("history [ -iedsl ]");
}
}
}
return 0;
}
#endif
/*
* Type function: type [ command ]
*
* For each name, indicate how it would be interpreted
*/
static int dowhence (int argc, char **argv)
{
char *cp;
int n; /* Argument count */
int inb; /* Inbuilt function */
bool v_flag;
bool p_flag = FALSE;
char *l_path;
FunctionList *fops;
AliasList *al;
/* Get some memory for the buffer */
if ((l_path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL)
return doOutofMemory (*argv);
v_flag = (bool)(strcmp (*argv, LIT_type) == 0);
ResetGetOptions (); /* Reset GetOptions */
while ((n = GetOptions (argc, argv, "pv", 0)) != EOF)
{
switch (n)
{
case 'v': v_flag = TRUE; break;
case 'p': p_flag = TRUE; break;
default:
return UsageError (BuiltinUsage);
}
}
/* Process each parameter */
while ((cp = argv[OptionIndex++]) != (char *)NULL)
{
/* Check for alias */
if ((al = LookUpAlias (cp, FALSE)) != (AliasList *)NULL)
{
if (v_flag)
printf ("%s is %s alias for %s\n", cp,
(al->Tracked) ? "a tracked" : "an", al->value);
else
puts (al->value);
}
/* Check for currently use inbuilt version */
else if (!p_flag && IsCommandBuiltIn (cp, &inb) && (inb & BLT_CURRENT))
{
if (v_flag)
printf (LIT_2Strings, cp, ShellInternalCommand);
else
puts (PATH_TO_UNIX (strlwr (l_path)));
}
/* Check for a function */
else if (!p_flag &&
((fops = LookUpFunction (cp)) != (FunctionList *)NULL))
{
if (v_flag)
printf (LIT_2Strings, cp, "is a function");
else
puts (cp);
}
/* Scan the path for an executable */
else if (FindLocationOfExecutable (l_path, cp) != EXTENSION_NOT_FOUND)
{
#ifdef OS2
if (!IsHPFSFileSystem (l_path))
strlwr (l_path);
#endif
if (v_flag)
printf (LIT_3Strings, cp, "is ",
PATH_TO_UNIX (strlwr (l_path)));
else
puts (PATH_TO_UNIX (strlwr (l_path)));
}
/* If not found, check for inbuilt version */
else if (!p_flag && (IsCommandBuiltIn (cp, &inb)))
{
if (v_flag)
printf (LIT_2Strings, cp, ShellInternalCommand);
else
puts (cp);
}
else if (!p_flag && LookUpSymbol (cp))
{
if (v_flag)
printf (LIT_2Strings, cp, "is a shell keyword");
else
puts (cp);
}
else if (v_flag)
PrintWarningMessage (LIT_2Strings, cp, NotFound);
}
return 0;
}
/*
* Table of internal commands. Note that this table is sort in alphabetic
* order.
*/
static struct builtin builtin[] = {
{ ".", dodot, (BLT_ALWAYS | BLT_CURRENT |
BLT_CENVIRON) },
{ ":", dolabel, (BLT_ALWAYS | BLT_CURRENT |
BLT_CENVIRON) },
{ "[", dotest, BLT_CURRENT },
{ "[[", dotest, (BLT_ALWAYS | BLT_CURRENT |
BLT_CENVIRON | BLT_NOGLOB) },
{ LIT_alias, doalias, (BLT_ALWAYS | BLT_CURRENT |
BLT_CENVIRON | BLT_NOGLOB |
BLT_NOWORDS) },
{ LIT_break, dobreak, (BLT_CURRENT | BLT_CENVIRON) },
{ LIT_builtin, dobuiltin, (BLT_ALWAYS | BLT_CURRENT) },
{ "cd", dochdir, BLT_CURRENT },
{ LIT_continue, docontinue, (BLT_CURRENT | BLT_CENVIRON) },
#ifdef OS2
{ "detach", dodetach, (BLT_ALWAYS | BLT_CURRENT) },
#endif
{ "echo", doecho, BLT_CURRENT },
{ "eval", doeval, (BLT_CURRENT | BLT_CENVIRON) },
{ LIT_exec, (int (*)(int, char **)) doexec,
(BLT_CURRENT | BLT_CENVIRON) },
{ LIT_exit, doexit, (BLT_CURRENT | BLT_CENVIRON) },
{ LIT_export, doexport, (BLT_CURRENT | BLT_CENVIRON |
BLT_NOGLOB | BLT_NOWORDS) },
#ifdef OS2
{ "extproc", dolabel, (BLT_CURRENT | BLT_CENVIRON) },
#endif
{ "false", dofalse, BLT_CURRENT },
{ "fc", dofc, BLT_CURRENT },
{ "functions", dofunctions, (BLT_ALWAYS | BLT_CURRENT) },
{ "getopts", dogetopts, BLT_CURRENT },
#ifndef NO_HISTORY
{ LIT_history, dohistory, BLT_CURRENT },
#endif
#ifdef OS2
{ "jobs", dojobs, BLT_CURRENT },
{ "kill", dokill, BLT_CURRENT },
#endif
{ "let", dolet, BLT_CURRENT },
{ "msdos", domsdos, BLT_CURRENT },
{ LIT_print, doecho, BLT_CURRENT },
{ "pwd", dopwd, BLT_CURRENT },
{ LIT_read, doread, BLT_CURRENT },
{ "readonly", doreadonly, (BLT_CURRENT | BLT_CENVIRON |
BLT_NOGLOB | BLT_NOWORDS) },
{ "return", doreturn, (BLT_CURRENT | BLT_CENVIRON) },
{ "set", doset, BLT_CURRENT },
{ LIT_shift, doshift, (BLT_CURRENT | BLT_CENVIRON) },
#ifdef OS2
{ "start", dostart, BLT_CURRENT },
#endif
{ "swap", doswap, BLT_CURRENT },
{ "test", dotest, BLT_CURRENT },
{ "trap", dotrap, (BLT_CURRENT | BLT_CENVIRON) },
{ "true", dolabel, BLT_CURRENT },
{ LIT_type, dowhence, BLT_CURRENT },
{ "typeset", dotypeset, (BLT_CURRENT | BLT_CENVIRON |
BLT_NOGLOB | BLT_NOWORDS) },
{ "umask", doumask, BLT_CURRENT },
{ LIT_unalias, dounalias, (BLT_ALWAYS | BLT_CURRENT) },
{ LIT_unfunction,
dounset, BLT_CURRENT },
{ "unset", dounset, BLT_CURRENT },
{ "ver", dover, BLT_CURRENT },
#ifdef OS2
{ "wait", dowait, BLT_CURRENT },
#endif
{ "whence", dowhence, BLT_CURRENT },
{ (char *)NULL, (int (*)())NULL, 0 }
};
/*
* Look up a built in command
*/
int (*IsCommandBuiltIn (register char *s, int *b))()
{
register struct builtin *bp;
int res;
/* Check for change drive */
*b = TRUE;
if ((strlen (s) == 2) && isalpha (*s) && (*s != '_') && (*(s + 1) == ':'))
return dodrive;
/* Search for command */
*b = FALSE;
for (bp = builtin; bp->command != (char *)NULL; bp++)
{
if ((res = stricmp (bp->command, s)) >= 0)
{
if (res != 0)
break;
*b = bp->mode;
return bp->fn;
}
}
return (int (*)())NULL;
}
/*
* Builtin - either list builtins or execute it
*
* builtin [-asd] [ names ]
*/
static int dobuiltin (int argc, char **argv)
{
register struct builtin *bp;
int (*shcom)(int, char **) = (int (*)())NULL;
int ReturnValue = 0;
char *carg;
int mode;
int action = -1;
int i;
if (argc < 2)
{
for (bp = builtin; bp->command != (char *)NULL; bp++)
printf (LIT_3Strings, LIT_builtin, bp->command,
(bp->mode & BLT_CURRENT) ? " - preferred" : "");
return 0;
}
/* Check for changing options */
ResetGetOptions (); /* Reset GetOptions */
while ((i = GetOptions (argc, argv, "sad", 0)) != EOF)
{
switch (i)
{
case 's': action = 0; break;
case 'a': action = 1; break;
case 'd': action = 2; break;
default:
return UsageError (BuiltinUsage);
}
}
/* Check to see if we know about the builtin version */
if (action == -1)
{
if ((shcom = IsCommandBuiltIn (argv[1], &mode)) == (int (*)())NULL)
{
printf (BasicErrorMessage, argv[1], NotBuiltinCommand);
return 1;
}
/* Yes - so execute the builtin version. Set up the word list correctly */
argv++;
ReturnValue = (*shcom)(CountNumberArguments (argv), argv);
argv--;
return ReturnValue;
}
/* Execute the requested functions against the builtin commands */
while ((carg = argv[OptionIndex++]) != (char *)NULL)
{
for (bp = builtin;
(bp->command != (char *)NULL) && (stricmp (bp->command, carg) != 0);
bp++)
continue;
/* Command is not builtin */
if (bp->command == (char *)NULL)
{
printf (BasicErrorMessage, carg, NotBuiltinCommand);
ReturnValue = 1;
continue;
}
/* Update on the action */
switch (action)
{
case 0:
printf (BasicErrorMessage, carg, (bp->mode & BLT_CURRENT)
? LIT_builtin : "external");
break;
case 1:
bp->mode |= BLT_CURRENT;
break;
case 2:
if (bp->mode & BLT_ALWAYS)
printf (BasicErrorMessage, carg, "always builtin");
else
bp->mode &= ~BLT_CURRENT;
break;
}
}
return ReturnValue;
}
/*
* Report Usage error
*/
static int near UsageError (char *string)
{
return PrintWarningMessage ("Usage: %s\n", string) + 1;
}
/*
* Alias command: alias [ -t] [ name [=commands] ]...
*/
static int doalias (int argc, char **argv)
{
int ReturnValue = 0;
int i = 1;
bool tracked = FALSE;
char *path = (char *)NULL;
char *cp;
/* Check for tracked aliases */
if ((argc > 1) && (strcmp (argv[1], "-t") == 0))
{
++i;
tracked = TRUE;
}
/* List only? */
if (argc <= i)
return PrintAllAlias (tracked);
/* Set them up or print them */
while (i < argc)
{
/* Not tracked - either set alias value if there is an equals or display
* the alias value
*/
if (!tracked)
{
if ((cp = strchr (argv[i], '=')) != (char *)NULL)
*(cp++) = 0;
/* Check for valid name */
if (!IsValidAliasName (argv[i], TRUE))
return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias,
argv[i]);
/* Save it if appropriate */
if (cp != (char *)NULL)
{
if (!SaveAlias (argv[i], cp, tracked))
ReturnValue = 1;
}
/* Print it */
else if (LookUpAlias (argv[i], FALSE) == (AliasList *)NULL)
{
PrintWarningMessage (NotAnAlias, LIT_alias, argv[i]);
ReturnValue = 1;
}
else
PrintAlias (argv[i]);
}
/* Set up tracked alias */
else if (!IsValidAliasName (argv[i], TRUE))
return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias,
argv[i]);
else if ((path == (char *)NULL) &&
((path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL))
return doOutofMemory (*argv);
/* Save the new path for the alias */
else if (SaveAlias (argv[i],
(FindLocationOfExecutable (path, argv[i])
!= EXTENSION_NOT_FOUND) ? path : null, tracked))
ReturnValue = 1;
++i;
}
return ReturnValue;
}
/*
* UnAlias command: unalias name...
*/
static int dounalias (int argc, char **argv)
{
AliasList *al;
int i;
/* Set them up or print them */
for (i = 1; i < argc; ++i)
{
if ((al = LookUpAlias (argv[i], FALSE)) != (AliasList *)NULL)
DeleteAlias (argv[i]);
else
PrintWarningMessage (NotAnAlias, LIT_unalias, argv[i]);
}
return 0;
}
/*
* OS2 detach function
*/
#ifdef OS2
static int dodetach (int argc, char **argv)
{
int RetVal;
if (argc < 2)
return UsageError ("detach program [ parameters ... ]");
argv++;
RetVal = ExecuteACommand (argv, EXEC_SPAWN_DETACH);
argv--;
return RetVal;
}
/*
* Start a session
*/
static int dostart (int argc, char **argv)
{
bool Direct = FALSE;
bool UseCMD = FALSE;
STARTDATA stdata;
Word_B *wb = (Word_B *)NULL;
int c;
/* Initialise the session control info */
stdata.FgBg = FALSE; /* Set background session */
stdata.Length = sizeof (STARTDATA);
stdata.PgmTitle = (char *)NULL;
stdata.Related = FALSE;
stdata.TraceOpt = 0;
stdata.TermQ = 0;
stdata.InheritOpt = 0;
stdata.IconFile = (char *)NULL;
stdata.PgmHandle = 0L;
stdata.PgmControl = 8;
stdata.InitXPos = 0;
stdata.InitYPos = 0;
stdata.InitXSize = 100;
stdata.InitYSize = 100;
/* These values get reset somewhere along the line */
stdata.SessionType = 2;
stdata.Environment = (char *)1; /* Build the environment */
/* Switch on the arguments */
ResetGetOptions (); /* Reset GetOptions */
while ((c = GetOptions (argc, argv, "t:dfWPFiC", 0)) != EOF)
{
switch (c)
{
case 't': /* Set title */
stdata.PgmTitle = OptionArgument;
if (strlen (OptionArgument) > 32)
OptionArgument[33] = 0;
break;
case 'd': /* Direct */
Direct = TRUE;
break;
case 'f': /* Foreground */
stdata.FgBg = TRUE;
break;
case 'W': /* PM Window */
stdata.SessionType = 2;
break;
case 'P': /* PM Session */
stdata.SessionType = 3;
break;
case 'F': /* Full Screen */
stdata.SessionType = 1;
break;
case 'C': /* Use the CMD processor */
UseCMD = TRUE;
break;
case 'i': /* Inherit environment */
stdata.Environment = (char *)NULL;
break;
default:
return UsageError ("start [ -dfWPFiC ] [ -t title ] [ program args..]");
}
}
/* Find the program to start */
if ((OptionIndex == argc) || (!Direct))
{
if (!UseCMD)
wb = AddWordToBlock (GetVariableAsString (ShellVariableName,
FALSE), wb);
else
{
wb = AddWordToBlock (GetVariableAsString (ComspecVariable,
FALSE), wb);
wb = AddWordToBlock ("/c", wb);
}
}
else
wb = AddWordToBlock (argv[OptionIndex++], wb);
SessionControlBlock = &stdata;
/* Build the argument block */
while (OptionIndex != argc)
wb = AddWordToBlock (argv[OptionIndex++], wb);
/* Start the session */
argv = GetWordList (AddWordToBlock (NOWORD, wb));
return (ExecuteACommand (argv, 0) == -1) ? 1 : 0;
}
#endif
/*
* Set up new Parameter Variables
*/
static int near SetUpNewParameterVariables (char **Array, int Offset, int Max,
char *function)
{
int n;
Word_B *wb = (Word_B *)NULL;
char *cp;
bool Duplicate = (bool)(strcmp (function, LIT_shift) == 0);
if ((wb = AddParameter (ParameterArray[0], wb, function)) == (Word_B *)NULL)
return 1;
for (n = Offset; n < Max; ++n)
{
if ((cp = (Duplicate) ? StringSave (Array[n]) : Array[n])
== null)
return doOutofMemory (function);
if ((wb = AddParameter (cp, wb, function)) == (Word_B *)NULL)
return 1;
}
return (AddParameter ((char *)NULL, wb, function) == (Word_B *)NULL)
? 1 : 0;
}
/*
* dolet - Each arg is an arithmetic expression to be evaluated.
*/
static int dolet (int argc, char **argv)
{
long Value = 0;
int i;
ExpansionErrorDetected = FALSE;
for (i = 1; !ExpansionErrorDetected && (argv[i] != (char *)NULL); ++i)
Value = EvaluateMathsExpression (argv[i]);
return !Value || ExpansionErrorDetected ? 1 : 0;
}
/*
* Out of memory error
*/
static int near doOutofMemory (char *s)
{
return PrintWarningMessage (BasicErrorMessage, s, Outofmemory1);
}
/*
* MSDOS, EXPORT and READONLY functions: xxxx [ variable names... ]
*/
static int doexport (int argc, char **argv)
{
return UpdateVariableStatus (argv + 1, STATUS_EXPORT);
}
static int doreadonly (int argc, char **argv)
{
return UpdateVariableStatus (argv + 1, STATUS_READONLY);
}
static int domsdos (int argc, char **argv)
{
return UpdateVariableStatus (argv + 1, STATUS_CONVERT_MSDOS);
}
static int near UpdateVariableStatus (char **argv, unsigned int Mask)
{
if (*argv == (char *)NULL)
return ListAllVariables (Mask, FALSE);
else
{
memset (&TypesetValues, 0, sizeof (TypesetValues));
TypesetValues.Flags_On = Mask;
return TypesetVariables (argv);
}
}
/*
* Sort Compare function for Environment variables.
*/
static int ComparisonForEnvironmentSort (VariableList **s1, VariableList **s2)
{
return strcmp ((*s1)->name, (*s2)->name);
}
/*
* List All variables matching a STATUS
*/
static int near ListAllVariables (unsigned int Mask, bool PrintValue)
{
HandleSECONDandRANDOM ();
DVE_Mask = Mask;
DVE_PrintValue = PrintValue;
twalk (VariableTree, DisplayVariableEntry);
return 0;
}
/*
* TWALK Function - display VARIABLE tree
*/
static void DisplayVariableEntry (void *key, VISIT visit, int level)
{
VariableList *vp = *(VariableList **)key;
if ((visit == postorder) || (visit == leaf))
{
if ((isalpha (*vp->name)) &&
((vp->status & DVE_Mask) ||
((vp->status == 0) && (DVE_Mask == 0xffff))))
PrintEntry (vp, DVE_PrintValue,
(DVE_Mask == 0xffff) ? 0 : DVE_Mask);
}
}
/*
* typeset function - [ [ [+-][Hflprtux] ] [+-][LRZi[n]] [ name [=value] ...]
*/
static int dotypeset (int argc, char **argv)
{
int ReturnValue = 0;
bool f_flag = FALSE;
unsigned int *CurrentFlags;
int tmp = 0;
char c_opt;
char *cp;
/* Initialise save area */
memset (&TypesetValues, 0, sizeof (TypesetValues));
OptionIndex = 1;
/* Scan the options */
while ((cp = argv[OptionIndex]) != (char *)NULL)
{
if ((*cp != '-') && (*cp != '+'))
break;
CurrentFlags = (*cp == '-') ? &TypesetValues.Flags_On
: &TypesetValues.Flags_Off;
while (*(++cp))
{
switch (*cp)
{
case 'p':
fprintf (stderr, "typeset: Option (%c) not supported\n",
*cp);
break;
case 'H':
*CurrentFlags |= STATUS_CONVERT_MSDOS;
break;
case 'f': /* Function only */
f_flag = TRUE;
break;
case 'l':
*CurrentFlags |= STATUS_LOWER_CASE;
break;
case 'r':
*CurrentFlags |= STATUS_READONLY;
break;
case 't':
*CurrentFlags |= STATUS_TAGGED;
break;
case 'u':
*CurrentFlags |= STATUS_UPPER_CASE;
break;
case 'x':
*CurrentFlags |= STATUS_EXPORT;
break;
case 'L':
case 'R':
case 'Z':
case 'i':
{
switch (c_opt = *cp)
{
case 'L':
*CurrentFlags |= STATUS_LEFT_JUSTIFY;
break;
case 'R':
*CurrentFlags |= STATUS_RIGHT_JUSTIFY;
break;
case 'Z':
*CurrentFlags |= STATUS_ZERO_FILL;
break;
case 'i':
*CurrentFlags |= STATUS_INTEGER;
break;
}
/* Only set width on on */
if (CurrentFlags != &TypesetValues.Flags_On)
break;
/* Check for following numeric */
if (isdigit (*(cp + 1)))
tmp = (int)strtol (cp + 1, &cp, 10);
else if ((*(cp + 1) == 0) &&
(OptionIndex + 1 < argc) &&
isdigit (*argv[OptionIndex + 1]))
tmp = (int)strtol (argv[++OptionIndex], &cp, 10);
else
break;
/* Check for invalid numeric */
if (!tmp || *(cp--))
return UsageError (TypeSetUsage);
/* Width or base */
if (c_opt == 'i')
TypesetValues.Base = tmp;
else
TypesetValues.Width = tmp;
break;
}
default:
return UsageError (TypeSetUsage);
}
}
++OptionIndex;
}
/* Check for f flag - function processing. */
if (f_flag)
{
if (((TypesetValues.Flags_On | TypesetValues.Flags_Off) &
~(STATUS_TAGGED | STATUS_EXPORT)) ||
(!(TypesetValues.Flags_On | TypesetValues.Flags_Off)))
return PrintWarningMessage ("typeset: Only -xt allowed with -f\n");
for (tmp = OptionIndex; tmp < argc; tmp++)
ReturnValue |= HandleFunction (argv[tmp]);
return ReturnValue;
}
/* Process variable assignments */
return TypesetVariables (&argv[OptionIndex]);
}
static int near TypesetVariables (char **argv)
{
bool PrintValue = TypesetValues.Flags_Off ? TRUE : FALSE;
VariableList *vp;
char *CurrentName;
char *NewValue;
char *OldValue;
int OldStatus;
long NValue;
char *xp;
int Retval = 0;
unsigned int Mask;
if ((Mask = (TypesetValues.Flags_On | TypesetValues.Flags_Off)) == 0)
Mask = 0xffff;
/* Switch off any appropriate flags */
if (TypesetValues.Flags_On & STATUS_LOWER_CASE)
TypesetValues.Flags_Off |= STATUS_UPPER_CASE;
if (TypesetValues.Flags_On & STATUS_UPPER_CASE)
TypesetValues.Flags_Off |= STATUS_LOWER_CASE;
if (TypesetValues.Flags_On & STATUS_RIGHT_JUSTIFY)
TypesetValues.Flags_Off |= STATUS_LEFT_JUSTIFY;
if (TypesetValues.Flags_On & STATUS_LEFT_JUSTIFY)
TypesetValues.Flags_Off |= STATUS_RIGHT_JUSTIFY;
/* If no arguments, print all values matching the mask */
if (*argv == (char *)NULL)
return ListAllVariables (Mask, PrintValue);
/* Process each argument. If no flags, print it */
while ((CurrentName = *(argv++)) != (char *)NULL)
{
if ((NewValue = strchr (CurrentName, '=')) != (char *)NULL)
*(NewValue++) = 0;
/* Check for a valid name */
if (IsValidVariableName (CurrentName))
{
Retval = PrintWarningMessage (BasicErrorMessage, CurrentName,
"bad identifier");
continue;
}
/* If valid - update, otherwise print a message */
if ((Mask != 0xffff) || (NewValue != (char *)NULL))
{
vp = LookUpVariable (CurrentName, TRUE);
OldStatus = vp->status;
OldValue = GetVariableAsString (CurrentName, FALSE);
/* Update status */
vp->status &= ~(TypesetValues.Flags_Off);
vp->status |= TypesetValues.Flags_On;
if (CurrentFunction != (FunctionList *)NULL)
vp->status |= STATUS_LOCAL;
/* Set up a new integer value. If the variable was not an integer
* originally and there is an error, unset it
*/
if (vp->status & STATUS_INTEGER)
{
if (ValidMathsExpression (xp = (NewValue != (char *)NULL)
? NewValue : OldValue,
&NValue))
{
Retval = PrintWarningMessage (LIT_Emsg, "bad numeric value",
CurrentName, xp);
if (!(OldStatus & STATUS_INTEGER))
UnSetVariable (CurrentName, FALSE);
continue;
}
/* Save the new integer value and set up base etc */
vp->nvalue = NValue;
if (!vp->base)
vp->base = (LastNumberBase != -1) ? LastNumberBase : 10;
if (TypesetValues.Base)
vp->base = TypesetValues.Base;
if (vp->value != null)
ReleaseMemoryCell ((void *)vp->value);
vp->value = null;
}
/* String - update if appropriate, both the value and the width */
else
{
SetVariableFromString (CurrentName,
(NewValue != (char *)NULL) ? NewValue
: OldValue);
}
if (TypesetValues.Width)
vp->width = TypesetValues.Width;
}
/* Print if appropriate */
else
PrintEntry (LookUpVariable (CurrentName, FALSE), PrintValue,
Mask);
}
return Retval;
}
static void near PrintEntry (VariableList *vp, bool PrintValue,
unsigned int Mask)
{
unsigned int Flags = vp->status & Mask;
if (vp->status & STATUS_NOEXISTANT)
return;
if (Flags & STATUS_INTEGER)
printf ("integer ");
if (Flags & STATUS_LEFT_JUSTIFY)
printf ("left justified %d ", vp->width);
if (Flags & STATUS_RIGHT_JUSTIFY)
printf ("right justified %d ", vp->width);
if (Flags & STATUS_ZERO_FILL)
printf ("zero filled %d ", vp->width);
if (Flags & STATUS_CONVERT_MSDOS)
printf ("MS-DOS Format ");
if (Flags & STATUS_LOWER_CASE)
printf ("lowercase ");
if (Flags & STATUS_UPPER_CASE)
printf ("uppercase ");
if (Flags & STATUS_READONLY)
printf ("readonly ");
if (Flags & STATUS_TAGGED)
printf ("tagged ");
if (Flags & STATUS_EXPORT)
printf ("exported ");
/* Print the value */
if (!PrintValue)
puts (vp->name);
else
printf (ListVarFormat, vp->name, GetVariableAsString (vp->name, TRUE));
}
/*
* Handle typeset -f
*/
static int near HandleFunction (char *name)
{
FunctionList *fop;
if (strchr (name, '=') != (char *)NULL)
return PrintWarningMessage ("typeset: cannot assign to functions\n");
if ((fop = LookUpFunction (name)) == (FunctionList *)NULL)
return PrintWarningMessage ("typeset: function %s does not exist\n",
name);
fop->Traced = (TypesetValues.Flags_On & STATUS_TAGGED) ? TRUE : FALSE;
return 0;
}
/*
* Modified version of getopt for shell
*/
static void near ResetGetOptions (void)
{
OptionIndex = 1; /* Reset the optind flag */
GetOptionPosition = 1; /* Current position */
}
int GetOptions (int argc, char **argv, char *optstring, int flags)
{
int cur_option; /* Current option */
char *cp; /* Character pointer */
if (GetOptionPosition == 1)
{
/* Special for doecho */
if (flags & GETOPT_PRINT)
return EOF;
/* Check for out of range, correct start character and not single */
if ((OptionIndex >= argc) ||
(!(((OptionStart = *argv[OptionIndex]) == '-') ||
(((flags & GETOPT_PLUS) && (OptionStart == '+'))))) ||
(!argv[OptionIndex][1]))
return EOF;
if ((!strcmp (argv[OptionIndex], DoubleHypen)) ||
((flags & GETOPT_PLUS) &&
(!strcmp (argv[OptionIndex], DoublePlus))))
return EOF;
}
/* Get the current character from the current argument vector */
cur_option = argv[OptionIndex][GetOptionPosition];
/* Validate it */
if ((cur_option == ':') ||
((cp = strchr (optstring, cur_option)) == (char *)NULL))
{
if (flags & GETOPT_MESSAGE)
PrintWarningMessage ("%s: illegal option -- %c\n", argv[0],
cur_option);
/* Save the bad option value and move to the next offset */
BadOptionValue = cur_option;
if (!argv[OptionIndex][++GetOptionPosition])
{
OptionIndex++;
GetOptionPosition = 1;
}
return '?';
}
/* Parameters following ? */
OptionArgument = (char *)NULL;
if (*(++cp) == ':')
{
if (argv[OptionIndex][GetOptionPosition + 1])
OptionArgument = &argv[OptionIndex++][GetOptionPosition + 1];
else if (++OptionIndex >= argc)
{
if (flags & GETOPT_MESSAGE)
PrintWarningMessage ("%s: option (%c) requires an argument\n",
argv[0], cur_option);
BadOptionValue = cur_option;
OptionArgument = (char *)-1;
GetOptionPosition = 1;
return '?';
}
else
OptionArgument = argv[OptionIndex++];
GetOptionPosition = 1;
}
else if (!argv[OptionIndex][++GetOptionPosition])
{
GetOptionPosition = 1;
OptionIndex++;
}
return cur_option;
}
#ifdef OS2
/*
* Kill the specified processes
*/
static struct SignalList {
char *Name;
int SigVal;
} SignalList [] = {
{"term", -1 }, {"usr1", PFLG_A },
{"usr2", PFLG_B }, {"usr3", PFLG_C },
{(char *)NULL, 0 }
};
static int dokill (int argc, char **argv)
{
int i;
int Sigv = -1;
char *cp;
PID pidProcess;
long value;
if (argc < 2)
return UsageError (KillUsage);
/* List signals ? */
if (!strcmp (argv[1], "-l"))
{
for (i = 0; SignalList[i].Name != (char *)NULL; ++i)
puts (SignalList[i].Name);
return 0;
}
/* Look up signal name */
if (**(++argv) == '-')
{
cp = &argv[0][1];
for (i = 0; SignalList[i].Name != (char *)NULL; ++i)
{
if (!stricmp (SignalList[i].Name, cp))
break;
}
if (SignalList[i].Name == (char *)NULL)
return PrintWarningMessage ("kill: bad signal name (%s)\n", cp);
Sigv = SignalList[i].SigVal;
if (*(++argv) == (char *)NULL)
return UsageError (KillUsage);
}
/* Kill the processes */
while (*argv != (char *)NULL)
{
/* Find the PID */
if (((**argv == '%') && !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
((**argv != '%') && !ConvertNumericValue (*argv, &value, 0)))
return PrintWarningMessage ("kill: bad process/job id (%s)\n",
*argv);
/* If Process ID, its in value */
if (**argv != '%')
pidProcess = (PID)value;
/* Send the signal */
if ((Sigv == -1) ? DosKillProcess (DKP_PROCESSTREE, pidProcess)
: DosFlagProcess (pidProcess, FLGP_SUBTREE, Sigv, 0))
return PrintWarningMessage ("kill: signal to process id (%s) failed\n",
*argv);
argv++;
}
return 0;
}
/*
* Wait for process to end
*/
static int dowait (int argc, char **argv)
{
PID pidProcess;
int TermStat;
int ReturnValue;
long value;
/* Check usage */
if (argc > 2)
return UsageError ("wait [ job ]");
/* Wait for all jobs ? */
if (argc < 2)
{
TermStat = -1;
/* Yes - wait until wait returns an error */
while ((pidProcess = wait (&ReturnValue)) != -1)
{
DeleteJob (pidProcess);
TermStat = ReturnValue;
}
}
/* Wait for a specific process. Job or PID? */
else
{
/* Move to the ID */
argv++;
/* Find the PID */
if (((**argv == '%') && !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
((**argv != '%') && !ConvertNumericValue (*argv, &value, 0)))
return PrintWarningMessage ("wait: bad process/job id (%s)\n",
*argv);
/* If Process ID, its in value */
if (**argv != '%')
pidProcess = (PID)value;
/* Wait for the specified process */
if (cwait (&TermStat, pidProcess, WAIT_GRANDCHILD) == -1)
return PrintWarningMessage ("wait: Process id (%s) not active\n",
*argv);
DeleteJob (pidProcess);
}
/* Convert termination status to return code */
if (TermStat == -1)
return -1;
else if (TermStat & 0x00ff)
return TermStat & 0x00ff;
else
return (TermStat >> 8) & 0x00ff;
}
/*
* Print the job info
*/
static int dojobs (int argc, char **argv)
{
bool ListMode = FALSE;
bool ListTree = FALSE;
pid_t p = getpid ();
long value;
int i;
/* List signals ? */
ResetGetOptions (); /* Reset GetOptions */
while ((i = GetOptions (argc, argv, "plP:", 0)) != EOF)
{
switch (i)
{
case 'l':
ListMode = TRUE;
break;
case 'p':
ListTree = TRUE;
break;
case 'P':
ListTree = TRUE;
if (!ConvertNumericValue (OptionArgument, &value, 0))
return PrintWarningMessage ("jobs: bad process id (%s)\n",
OptionArgument);
p = (pid_t)value;
break;
default:
return UsageError (JobUsage);
}
}
if (ListTree)
return PrintProcessTree (p);
/* Look up job name */
return PrintJobs (ListMode);
}
#endif
/*
* Change option value
*/
static bool near ChangeOptionValue (char *value, bool set)
{
struct SetOptions *entry = LookUpOptionValue (value);
if (entry == (struct SetOptions *)NULL)
return FALSE;
else if (entry->HasOptionValue)
SetClearFlag (entry->FlagValue, set);
else if (set)
GlobalFlags |= entry->FlagValue;
else
GlobalFlags &= ~(entry->FlagValue);
return TRUE;
}
/*
* Update shell switches
*/
static void near SetClearFlag (int Flag, bool set)
{
if (set)
FL_SET (Flag);
else
FL_CLEAR (Flag);
SetShellSwitches ();
}
/*
* Test an option
*/
static int near TestOptionValue (char *value)
{
struct SetOptions *entry = LookUpOptionValue (value);
if (entry == (struct SetOptions *)NULL)
{
PrintWarningMessage ("%s: unknown option - %s\n", TestProgram, value);
longjmp (TestErrorReturn, 1);
}
else if (entry->HasOptionValue)
return (FL_TEST (entry->FlagValue) != 0);
else
return (GlobalFlags & entry->FlagValue);
}
/*
* Find an Option entry
*/
static struct SetOptions * near LookUpOptionValue (char *value)
{
int i = 0;
char *cp;
while ((cp = SetOptions[i].OptionName) != (char *)NULL)
{
if (!strcmp (cp, value))
return &SetOptions[i];
++i;
}
return (struct SetOptions *)NULL;
}
/*
* Get Unit number
*/
static int near GetUnitNumber (char *prog)
{
int Unit;
if (((Unit = GetNumericValue (OptionArgument)) < 0) || (Unit > 9))
{
PrintWarningMessage (LIT_Emsg, LIT_bun, prog, OptionArgument);
return -1;
}
return Unit;
}
/*
* fc function - fc [-e EditorName] [-nlr] [First [Last]]
* fc -e - [Old=New] [Command]
*/
static int dofc (int argc, char **argv)
{
char *Editor = GetVariableAsString ("FCEDIT", FALSE);
bool n_flag = TRUE;
bool l_flag = FALSE;
bool r_flag = FALSE;
int EventNumber[2];
char *Temp;
char *Change = (char *)NULL;
char *Change1;
char *NewBuffer;
char *NewArg[3];
int i;
FILE *fp;
/* Check status */
if (!Interactive ())
return PrintWarningMessage ("fc: only available in interactive mode\n");
if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 1)) == (char *)NULL)
return doOutofMemory ("fc");
/* Process options */
ResetGetOptions (); /* Reset GetOptions */
while ((i = GetOptions (argc, argv, "e:nlr", 0)) != EOF)
{
switch (i)
{
case 'e': /* editor name */
Editor = OptionArgument;
break;
case 'n': n_flag = FALSE; break;
case 'l': l_flag = TRUE; break;
case 'r': r_flag = TRUE; break;
default:
return UsageError ("fc [ -e EditorName ] [ -nlr ] [ First [Last]]\n fc -e - [ Old=New ] [ Command ]");
}
}
argv += OptionIndex;
argc -= OptionIndex;
/* Check for [old=new] */
if (argc && ((Change1 = strchr (*argv, '=')) != (char *)NULL))
{
Change = *(argv++);
*(Change1++) = 0;
--argc;
}
if (!l_flag)
DeleteLastHistory ();
/* Get the first and last event number */
for (i = 0; i < 2; i++)
{
EventNumber[i] = 0;
if (argc)
{
EventNumber[i] = (int)strtol (*argv, &Temp, 10);
if (*Temp)
EventNumber[i] = SearchHistory (*argv);
else if (EventNumber[i] < 0)
EventNumber[i] += GetLastHistoryEvent () - 1;
if (EventNumber[i] <= 0)
return PrintWarningMessage ("fc: event <%s> not found\n",
*argv);
argv++;
--argc;
}
}
/* Set up first and last values */
i = GetLastHistoryEvent () - 1;
if (!EventNumber[0])
{
if ((EventNumber[0] = (l_flag) ? i - 16 : i) <= 0)
EventNumber[0] = 1;
}
if (!EventNumber[1])
EventNumber[1] = (l_flag) ? i : EventNumber[0];
/* If l - print */
if (l_flag)
fp = stdout;
else if (Editor == null)
return PrintWarningMessage ("fc: editor not defined\n");
else if ((fp = fopen ((Temp = GenerateTemporaryFileName ()), "w+b"))
== (FILE *)NULL)
return PrintWarningMessage ("fc: cannot create %s\n", Temp);
/* Process history */
if (!l_flag)
n_flag = FALSE;
PrintHistory (r_flag, n_flag, EventNumber[0], EventNumber[1], fp);
if (l_flag)
return 0;
/* Check that we found some history */
if (!ftell (fp))
l_flag = TRUE;
fclose (fp);
if (l_flag)
{
unlink (Temp);
return PrintWarningMessage ("fc: no matches\n");
}
/* Invoke the editor */
if (strcmp (Editor, ShellOptionsVariable))
{
NewArg[0] = Editor;
NewArg[1] = Temp;
NewArg[2] = (char *)NULL;
if (ExecuteACommand (NewArg, 0) == -1)
{
unlink (Temp);
return 1;
}
}
/* Now execute it */
if ((i = S_open (TRUE, Temp, O_RMASK)) < 0)
return PrintWarningMessage ("fc: cannot re-open edit file (%s)\n",
Temp);
argc = read (i, NewBuffer, LINE_MAX - 1);
S_close (i, TRUE);
if (argc <= 0)
return (argc == 0) ? 0 : 1;
else if (argc >= LINE_MAX - 1)
return PrintWarningMessage (FCTooLong);
/* Strip off trailing EOFs and EOLs */
CleanUpBuffer (argc, NewBuffer, 0x1a);
/* Check for substitution */
if (Change == (char *)NULL)
strcpy (ConsoleLineBuffer, NewBuffer);
else
{
if ((Temp = strstr (NewBuffer, Change)) == (char *)NULL)
return PrintWarningMessage ("fc: string not found");
if ((i = strlen (NewBuffer) - strlen (Change) +
strlen (Change1)) >= LINE_MAX - 2)
return PrintWarningMessage (FCTooLong);
/* Do the substitution */
i = Temp - NewBuffer;
strncpy (ConsoleLineBuffer, NewBuffer, i);
strcpy (ConsoleLineBuffer + i, Change1);
strcat (ConsoleLineBuffer, NewBuffer + strlen (Change) + i);
}
ReleaseMemoryCell ((void *)NewBuffer);
/* Tell the user what we've done */
puts (ConsoleLineBuffer);
/* Flag the console driver not to read from the console, but use the
* current contents of the ConsoleLineBuffer
*/
UseConsoleBuffer = TRUE;
return 0;
}
/*
* Strip off trailing EOFs and EOLs from console buffer
*/
static char near CleanUpBuffer (int length, char *buffer, int eofc)
{
char *cp = &buffer[length - 1];
char ret;
while (length && ((*cp == (char)eofc) || (*cp == CHAR_NEW_LINE)))
{
length--;
cp--;
}
ret = *(cp + 1);
*(cp + 1) = 0;
return ret;
}
/*
* Convert Job ID to process id
*/
#ifdef OS2
static bool near ConvertJobToPid (char *String, PID *pid)
{
long value;
JobList *jp;
/* If numeric value, look up the job number */
if (ConvertNumericValue (String, &value, 0))
jp = LookUpJob ((int)value);
else
jp = SearchForJob (String);
if (jp == (JobList *)NULL)
return FALSE;
PreviousJob = CurrentJob;
CurrentJob = jp->pid;
*pid = jp->pid;
return TRUE;
}
#endif